package com.cmb.lk50.utils;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Author       : 蒋俊钊 80256973
 * Date         : 2019/5/10
 * Copyright    (C) ChinaMerchantsBank
 */
public class ReflectUtil {

    /**
     *  获取当前路径及其子目录下所有子类或接口实现类 (不包含自身)
     */
    public static <T> Set<Class<T>> getAllAssignedClass(Class<T> cls) throws ClassNotFoundException, IOException {
        return getAllAssignedClass(cls, cls);
    }

    /**
     *  获取当前路径及其子目录下所有 指定类型 的子类或接口实现类 (不包含自身)
     *  cls 所在目录的类
     *  assignedCls  指定类型
     */
    public static <T> Set<Class<T>> getAllAssignedClass(Class<?> cls, Class<T> assignedCls) throws ClassNotFoundException, IOException {
        Set<Class<T>> classes = new HashSet<>();
        for (Class c : getClasses(cls)) {
            if (assignedCls.isAssignableFrom(c) && !assignedCls.equals(c)) {
                classes.add(c);
            }
        }
        return classes;
    }

    /**
     *  取得当前路径及其子目录下的所有类
     */
    public static Set<Class<?>> getClasses(Class<?> cls) throws ClassNotFoundException, IOException {
        String pk = cls.getPackage().getName();
        String path = pk.replace('.', '/');

        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resources = classloader.getResources(path);

        HashSet<Class<?>> set = new HashSet<>();
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            String protocol = url.getProtocol();
            if ("file".equals(protocol)) {
                set.addAll(getClasses(new File(url.getFile()), pk, set));
            } else if ("jar".equals(protocol)) {
                JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                set.addAll(getClasses(jarFile, cls, path, set));
            }
        }

        return set;
    }


    /**
     *  迭代所有类生成class
     *  用于本地开发调试，还没打成jar包，以文件形式存在
     */
    private static Set<Class<?>> getClasses(File dir, String pk, Set<Class<?>> classes) throws ClassCastException, ClassNotFoundException {
        File[] files = dir.listFiles();
        if (!dir.exists() || files == null) {
            return classes;
        }

        for (File f : files) {
            if (f.isDirectory()) {
                classes.addAll(getClasses(f, pk + "." + f.getName(), classes));
            } else if (f.getName().endsWith(".class")) {
                classes.add(Class.forName(pk + "." + f.getName().substring(0, f.getName().length() - 6)));
            }
        }
        return classes;
    }


    /**
     *  迭代所有类生成class
     *  jar包内查找
     */
    private static Set<Class<?>> getClasses(JarFile jarFile, Class<?> cls, String path, Set<Class<?>> classes) throws ClassNotFoundException {
        Enumeration<JarEntry> entries = jarFile.entries();
        String parentClazz = cls.getName().replace('.', '/');
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            if (name.startsWith(path) && name.endsWith(".class") &&
              !name.startsWith(parentClazz) &&  // 排除抽象父类
              !name.contains("$")) {                                                    // 排除内部类
                classes.add(Class.forName(name.replace("/", ".").substring(0, name.length() - 6)));
            }
        }
        return classes;
    }
}
